from math import sin, cos, sqrt, atan2, pi
from random import random
from pyglet.gl import *
from awesum import Vector3

class Camera():
	MOVE_RATIO = .9		#Reduce the camera moveTo dist each step
	SHAKE_PYR_LIMIT = Vector3(1, 2, 1)
	
	def __init__(self):
		#Initialize all 3 vectors
		self.pos = self.view = self.up = Vector3()
		self.shake = False
		self._shakeV = None
	
	def set(self, p, v, u, shake=False):
		self.pos = p	#Position, Vector3
		self.view = v	#View, Vector3
		self.up = u		#Up, Vector3
		if shake:
			self.shake = shake
			self.isShaking = False
			self.shakeQuantity = Vector3(random() * 8+6, random() * 8+6, random() * 8+6)
			self.shakeOsc = 0 #oscillator, 0 to 2pi for equations
			self._setRandShake()
	
	#look - call the actual OpenGL transforming function
	def look(self):
		if self.shake and self.isShaking:
			self._stepShake()
			pos = self.pos + self.shakeV
			view = self.view + self.shakeV
			gluLookAt(pos.x, pos.y, pos.z,
				  view.x, view.y, view.z,
				  self.up.x, self.up.y, self.up.z)
		else:
			gluLookAt(self.pos.x, self.pos.y, self.pos.z,
				  self.view.x, self.view.y, self.view.z,
				  self.up.x, self.up.y, self.up.z)
	
	#moveTo - Gradually move the camera closer to a point. This is
	#	meant to be used in a Step call to make the movement fluid.
	#	Since it's the same ratio every step, it slows as it
	#	approaches the destination.
	#	The MOVE_RATIO could be user specified in the future.
	def moveTo(self, p, maxDist):
		dist = self.pos.distFrom(p)
		if dist > maxDist:		#The cam can only be so far from the point
			newDist = maxDist
			if self.shake: self.isShaking = True
		else:					#Cam is within the range to glide to a stop
			newDist = dist * Camera.MOVE_RATIO
			if self.shake: self.isShaking = False
			
		#print dist
		diff = self.pos - p				#distance from cam x,z to point x,z
		a = atan2(diff.x, diff.z)	#angle between cam and point
		
		#Find the hypotenuse of the ground-level (x,z) distance
		hyp = sqrt((diff.x)**2 + (diff.z)**2)
		#Now find the angle between the hypotenuse and the y difference
		yAng = atan2(diff.y, hyp)
		
		#t=1
		#if yAng > .25: t=.9 #this lurches the camera forward on high angles, only works on XZ plane
		#Set the cam pos relative to (point+newer limited following distance)
		self.pos.x = p.x + newDist * sin(a) * cos(yAng) # * t
		self.pos.z = p.z + newDist * cos(a) * cos(yAng) # * t
		self.pos.y = p.y + newDist * sin(yAng)
	
	#unused
	def strafeCamera(self, speed):
		relView = self.view.minusV(self.pos)
		
		#Orthogonal vector for the view vector
		vOrthoVector = Vector3(-relView.z, 0, relView.x)

		#left positive cameraspeed and right negative -cameraspeed.
		self.pos.add(vOrthoVector.multiply(speed))
	
	#unused
	def moveCameraY(self, speed):
		relView = self.view.minusV(self.pos)

		self.view.y = self.pos.y + sin(speed) * relView.z + cos(speed) * relView.y
		self.view.z = self.pos.z + cos(speed) * relView.z - sin(speed) * relView.y
		self.view.x = self.pos.x + cos(speed) * relView.x - sin(speed) * relView.y
	
	#unused
	def moveCameraZ(self, speed): #move fwd and backward
		relView = self.view.minusV(self.pos)

		#forward positive camera speed and backward negative camera speed.
		relView.multiply(speed)
		self.pos.add(relView)
		self.view.add(relView)
	
	#unused
	def rotateView(self, speed):
		relView = self.view.minusV(self.pos)

		self.view.z = self.pos.z + sin(speed) * relView.x + cos(speed) * relView.z
		self.view.x = self.pos.x + cos(speed) * relView.x - sin(speed) * relView.z
	
	def _setRandShake(self):
		self.shakeScale = Vector3(random() * .025, random() * .025, random() * .025)
	
	def _stepShake(self):
		self.shakeOsc += .1
		if self.shakeOsc >= pi:
			self.shakeOsc %= 2 * pi
			self._setRandShake()
		self.shakeV = Vector3(
			self.shakeScale.x * sin(self.shakeQuantity.x * self.shakeOsc),
			self.shakeScale.y * sin(self.shakeQuantity.y * self.shakeOsc),
			self.shakeScale.z * sin(self.shakeQuantity.z * self.shakeOsc)
		)
